package com.cmc.cloud.cmclink.doc.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmc.cloud.cmclink.doc.api.document.emums.NumOperateTypeEnum;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.VoyageBsaCbaErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.VoyageBsaErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.VoyageBsaHgErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.entity.VoyageBsaCbaDetailDO;
import com.cmc.cloud.cmclink.doc.entity.VoyageBsaCbaTemplateDO;
import com.cmc.cloud.cmclink.doc.entity.VoyageBsaDO;
import com.cmc.cloud.cmclink.doc.entity.VoyageBsaHgDO;
import com.cmc.cloud.cmclink.doc.mapper.VoyageBsaCbaDetailMapper;
import com.cmc.cloud.cmclink.doc.mapper.VoyageBsaCbaTemplateMapper;
import com.cmc.cloud.cmclink.doc.mapper.VoyageBsaMapper;
import com.cmc.cloud.cmclink.doc.vo.voyagebsacbavo.VoyageBsaCbaBaseVO;
import com.cmc.cloud.cmclink.doc.vo.voyagebsacbavo.VoyageBsaCbaCreateReqVO;
import com.cmc.cloud.cmclink.doc.vo.voyagebsacbavo.VoyageBsaCbaPageReqVO;
import com.cmc.cloud.cmclink.doc.vo.voyagebsacbavo.VoyageBsaCbaQuantityVO;
import com.cmc.cloud.cmclink.doc.vo.voyagebsacbavo.VoyageBsaCbaRespVO;
import com.cmc.cloud.cmclink.doc.vo.voyagebsacbavo.VoyageBsaCbaUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.voyagebsahgvo.VoyageBsaHgBaseVO;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;

import java.util.*;

import com.cmc.cloud.cmclink.doc.service.VoyageBsaCbaService;
import com.cmc.cloud.cmclink.doc.entity.VoyageBsaCbaDO;
import com.cmc.cloud.cmclink.doc.mapper.VoyageBsaCbaMapper;

/**
 * Class-based Allocation分类舱位 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
public class VoyageBsaCbaServiceImpl implements VoyageBsaCbaService {

    @Resource
    private VoyageBsaCbaMapper voyageBsaCbaMapper;

    @Resource
    private VoyageBsaCbaDetailMapper voyageBsaCbaDetailMapper;

    @Resource
    private VoyageBsaMapper voyageBsaMapper;

    @Resource
    private VoyageBsaCbaTemplateMapper voyageBsaCbaTemplateMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Lock4j(name = "voyageBsaCbaUnique",
            keys = {"#createReqVO.getVoyageBsaId", "#createReqVO.getSpaceTypeCode", "#createReqVO.getAllocationSpace"})
    public void createVoyageBsaCba(VoyageBsaCbaCreateReqVO createReqVO) {
        // 插入
        if (voyageBsaMapper.selectCount(
                new LambdaQueryWrapperX<VoyageBsaDO>().eq(VoyageBsaDO::getId, createReqVO.getVoyageBsaId())) != 0L) {
            if (ObjectUtil.isEmpty(createReqVO.getRemainAllocationSpace())) {
                throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_REMAIN_NOT_EXISTED);
            }
        }
        validateUniqueExists(createReqVO, null);
        // 模板数据主信息 舱位数和冷插数 不需要 和子信息 汇总校验
        if (voyageBsaCbaTemplateMapper.selectCount(new LambdaQueryWrapperX<VoyageBsaCbaTemplateDO>()
                .eq(VoyageBsaCbaTemplateDO::getId, createReqVO.getVoyageBsaId())) == 0L) {
            //bsa主信息
            VoyageBsaDO voyageBsaDO = voyageBsaMapper.selectById(createReqVO.getVoyageBsaId());
            if (voyageBsaDO == null) {
                throw ServiceExceptionUtil.exception(VoyageBsaErrorCodeConstants.VOYAGE_BSA_NOT_EXISTS);
            }
            List<VoyageBsaCbaDO> voyageBsaCbaDos = voyageBsaCbaMapper.selectList(new LambdaQueryWrapperX<VoyageBsaCbaDO>()
                    .eq(VoyageBsaCbaDO::getVoyageBsaId, createReqVO.getVoyageBsaId()));
            //校验舱位分配是否超过总额
            if (voyageBsaDO.getBsaTeu()
                    .compareTo(voyageBsaCbaDos.stream().mapToInt(VoyageBsaCbaDO::getAllocationSpace).sum() + createReqVO.getAllocationSpace()) < 0) {
                throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CAB_TEU_NOT_EQUAL);
            }
            //校验冷插数是否超过总额
            if (voyageBsaDO.getRfUnit()
                    .compareTo(voyageBsaCbaDos.stream().mapToInt(VoyageBsaCbaDO::getRfUnit).sum() + createReqVO.getRfUnit()) < 0) {
                throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CAB_RF_NOT_EQUAL);
            }
        }
        VoyageBsaCbaDO voyageBsaCba = BeanUtil.copyProperties(createReqVO, VoyageBsaCbaDO.class);
        voyageBsaCbaMapper.insert(voyageBsaCba);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Lock4j(name = "voyageBsaCbaUnique",
            keys = {"#updateReqVO.getVoyageBsaId", "#updateReqVO.getSpaceTypeCode", "#updateReqVO.getAllocationSpace"})
    public void updateVoyageBsaCba(VoyageBsaCbaUpdateReqVO updateReqVO) {
        // 校验存在
        if (voyageBsaMapper.selectCount(
                new LambdaQueryWrapperX<VoyageBsaDO>().eq(VoyageBsaDO::getId, updateReqVO.getVoyageBsaId())) != 0L) {
            if (ObjectUtil.isEmpty(updateReqVO.getRemainAllocationSpace())) {
                throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_REMAIN_NOT_EXISTED);
            }
        }
        validateVoyageBsaCbaExists(updateReqVO.getId());
        validateUniqueExists(updateReqVO, updateReqVO.getId());
        // 更新
        VoyageBsaCbaDO updateObj = BeanUtil.copyProperties(updateReqVO, VoyageBsaCbaDO.class);
        voyageBsaCbaMapper.updateById(updateObj);
    }

    @Override
    public void deleteVoyageBsaCba(Long id) {
        // 校验存在
        VoyageBsaCbaDO bsaCbaDO = voyageBsaCbaMapper.selectById(id);
        if (bsaCbaDO == null) {
            throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CBA_NOT_EXISTS);
        }
        if (bsaCbaDO.getRemainAllocationSpace() != null && bsaCbaDO.getAllocationSpace().compareTo(bsaCbaDO.getRemainAllocationSpace()) != 0) {
            if (voyageBsaMapper.selectCount(new LambdaQueryWrapper<VoyageBsaDO>().eq(VoyageBsaDO::getId, bsaCbaDO.getVoyageBsaId())) != 0) {
                throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CAB_NOT_DELETE);
            }
        }
        // 删除
        voyageBsaCbaMapper.deleteById(id);
        voyageBsaCbaDetailMapper
                .delete(new LambdaQueryWrapper<VoyageBsaCbaDetailDO>().eq(VoyageBsaCbaDetailDO::getVoyageBsaCbaId, id));
    }

    private void validateVoyageBsaCbaExists(Long id) {
        if (voyageBsaCbaMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CBA_NOT_EXISTS);
        }
    }

    private void validateUniqueExists(VoyageBsaCbaBaseVO vo, Long id) {
        LambdaQueryWrapperX<VoyageBsaCbaDO> queryWrapperX =
                new LambdaQueryWrapperX<VoyageBsaCbaDO>().eqIfPresent(VoyageBsaCbaDO::getVoyageBsaId, vo.getVoyageBsaId())
                        .eqIfPresent(VoyageBsaCbaDO::getSpaceIntruction, vo.getSpaceIntruction())
                        .eqIfPresent(VoyageBsaCbaDO::getSpaceTypeCode, vo.getSpaceTypeCode());
        if (id != null) {
            queryWrapperX.ne(VoyageBsaCbaDO::getId, id);
        }
        if (voyageBsaCbaMapper.selectCount(queryWrapperX) != 0L) {
            throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CBA_EXISTED);
        }
    }

    @Override
    public VoyageBsaCbaDO getVoyageBsaCba(Long id) {
        return voyageBsaCbaMapper.selectById(id);
    }

    @Override
    public List<VoyageBsaCbaDO> getVoyageBsaCbaList(Collection<Long> ids) {
        return voyageBsaCbaMapper.selectBatchIds(ids);
    }

    @Override
    public List<VoyageBsaCbaRespVO> getByMainId(Long voyageBsaId, String spaceTypeCode) {
        List<VoyageBsaCbaDO> voyageBsaCbaDos = voyageBsaCbaMapper.selectList(new LambdaQueryWrapper<VoyageBsaCbaDO>()
                .eq(VoyageBsaCbaDO::getVoyageBsaId, voyageBsaId).eq(VoyageBsaCbaDO::getSpaceTypeCode, spaceTypeCode));
        List<VoyageBsaCbaRespVO> voyageBsaCbaRespVos = BeanUtil.copyToList(voyageBsaCbaDos, VoyageBsaCbaRespVO.class);
        return voyageBsaCbaRespVos;
    }

    @Override
    public PageResult<VoyageBsaCbaDO> getVoyageBsaCbaPage(VoyageBsaCbaPageReqVO reqVO) {
        IPage<VoyageBsaCbaDO> page = Page.of(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<VoyageBsaCbaDO> result = voyageBsaCbaMapper.selectPageList(page, reqVO);
        return new PageResult<VoyageBsaCbaDO>(result.getRecords(), result.getTotal());
    }

    @Override
    public List<VoyageBsaCbaDO> getByMainIds(List<Long> voyageBsaIds) {
        LambdaQueryWrapperX<VoyageBsaCbaDO> queryWrapperX =
                new LambdaQueryWrapperX<VoyageBsaCbaDO>().in(VoyageBsaCbaDO::getVoyageBsaId, voyageBsaIds);
        return voyageBsaCbaMapper.selectList(queryWrapperX);
    }

    @Override
    public void updateBatchVoyageBsaCba(List<VoyageBsaCbaDO> voyageBsaDOList) {
        voyageBsaCbaMapper.updateBatch(voyageBsaDOList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void operateBsaSpaceNum(VoyageBsaCbaQuantityVO bsaCbaQuantityVO) {
        VoyageBsaCbaDO bsaCbaDO = voyageBsaCbaMapper.selectById(bsaCbaQuantityVO.getId());
        VoyageBsaDO voyageBsaDO = voyageBsaMapper.selectById(bsaCbaDO.getVoyageBsaId());
        if (voyageBsaDO == null) {
            throw ServiceExceptionUtil.exception(VoyageBsaErrorCodeConstants.VOYAGE_BSA_NOT_EXISTS);
        }
        bsaCbaDO.setSpaceIntruction(bsaCbaQuantityVO.getSpaceIntruction());
        List<VoyageBsaCbaDO> voyageBsaCbaDos = voyageBsaCbaMapper.selectList(new LambdaQueryWrapperX<VoyageBsaCbaDO>()
                .ne(VoyageBsaCbaDO::getId, bsaCbaDO.getId())
                .eq(VoyageBsaCbaDO::getVoyageBsaId, bsaCbaDO.getVoyageBsaId()));
        if (ObjectUtil.isNotNull(bsaCbaQuantityVO.getAllocationNum())) {
            if (NumOperateTypeEnum.ADD.getValue().equals(bsaCbaQuantityVO.getNumOperateType())) {
                Integer allocationResult = Math.addExact(bsaCbaDO.getAllocationSpace(), bsaCbaQuantityVO.getAllocationNum());
                if (CollectionUtil.isNotEmpty(voyageBsaCbaDos)) {
                    if (voyageBsaDO.getBsaTeu()
                            .compareTo(voyageBsaCbaDos.stream().mapToInt(VoyageBsaCbaDO::getAllocationSpace).sum() + allocationResult) < 0) {
                        throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CAB_TEU_NOT_EQUAL);
                    }
                } else {
                    if (voyageBsaDO.getBsaTeu().compareTo(allocationResult) < 0) {
                        throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CAB_TEU_NOT_EQUAL);
                    }
                }
                bsaCbaDO
                        .setAllocationSpace(allocationResult);
                bsaCbaDO.setRemainAllocationSpace(
                        Math.addExact(bsaCbaDO.getRemainAllocationSpace(), bsaCbaQuantityVO.getAllocationNum()));
            } else if (NumOperateTypeEnum.SUB.getValue().equals(bsaCbaQuantityVO.getNumOperateType())) {
                bsaCbaDO.setAllocationSpace(
                        Math.subtractExact(bsaCbaDO.getAllocationSpace(), bsaCbaQuantityVO.getAllocationNum()));
                bsaCbaDO.setRemainAllocationSpace(
                        Math.subtractExact(bsaCbaDO.getRemainAllocationSpace(), bsaCbaQuantityVO.getAllocationNum()));
            }
        }
        if (ObjectUtil.isNotNull(bsaCbaQuantityVO.getRfNum())) {
            if (NumOperateTypeEnum.ADD.getValue().equals(bsaCbaQuantityVO.getRfNumOperateType())) {
                Integer rfUnitResult = Math.addExact(bsaCbaDO.getRfUnit(), bsaCbaQuantityVO.getRfNum());
                if (CollectionUtil.isNotEmpty(voyageBsaCbaDos)) {
                    if (voyageBsaDO.getRfUnit()
                            .compareTo(voyageBsaCbaDos.stream().mapToInt(VoyageBsaCbaDO::getRfUnit).sum() + rfUnitResult) < 0) {
                        throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CAB_RF_NOT_EQUAL);
                    }
                } else {
                    if (voyageBsaDO.getRfUnit().compareTo(rfUnitResult) < 0) {
                        throw ServiceExceptionUtil.exception(VoyageBsaCbaErrorCodeConstants.VOYAGE_BSA_CAB_RF_NOT_EQUAL);
                    }
                }
                bsaCbaDO
                        .setRfUnit(rfUnitResult);
                bsaCbaDO.setRemainRfUnit(
                        Math.addExact(bsaCbaDO.getRemainRfUnit(), bsaCbaQuantityVO.getRfNum()));
            } else if (NumOperateTypeEnum.SUB.getValue().equals(bsaCbaQuantityVO.getRfNumOperateType())) {
                bsaCbaDO.setRfUnit(
                        Math.subtractExact(bsaCbaDO.getRfUnit(), bsaCbaQuantityVO.getRfNum()));
                bsaCbaDO.setRemainRfUnit(
                        Math.subtractExact(bsaCbaDO.getRemainRfUnit(), bsaCbaQuantityVO.getRfNum()));
            }
        }
        voyageBsaCbaMapper.updateById(bsaCbaDO);
    }
}
